﻿using System;
using Pfz.Threading;

namespace Pfz.Collections
{
	/// <summary>
	/// This enumerator returns a "next value" every time the actual value is set, but
	/// if many sets are done before the client is able to process them, the 
	/// intermediate values are lost, so only the "actual" one is got.
	/// </summary>
	/// <typeparam name="T">The type of the values.</typeparam>
	public class ActualValueEnumerator<T>:
		ReaderWriterSafeDisposable,
		IFastEnumerator<T>
	where
		T: class
	{
		private ManagedAutoResetEvent _autoResetEvent = new ManagedAutoResetEvent();

		#region Dispose
			/// <summary>
			/// Only sets the event so any waiting thread is free.
			/// </summary>
			protected override void Dispose(bool disposing)
			{
				if (disposing)
				{
					_actualValue = null;

					Disposer.Dispose(ref _autoResetEvent);
				}
				
				base.Dispose(disposing);
			}
		#endregion
		
		#region Property ActualValue
			private T _actualValue;
			/// <summary>
			/// Gets or sets the actual value.
			/// </summary>
			public T ActualValue
			{
				get
				{
                    using(DisposeLock.ReadLock())
    					if (!WasDisposed)
							return _actualValue;

                    return null;
				}
				set
				{
                    if (value == null)
                        throw new ArgumentNullException("value");

					using(DisposeLock.WriteLock())
					{
						CheckUndisposed();
							
						_actualValue = value;
						_autoResetEvent.Set();
					}
				}
			}
		#endregion
		#region Methods
			#region GetNext
				/// <summary>
				/// Gets the next value.
				/// </summary>
				public T GetNext()
				{
                    var autoResetEvent = _autoResetEvent;
                    if (autoResetEvent == null)
                        return null;

                    autoResetEvent.WaitOne();

                    using(DisposeLock.ReadLock())
                    {
                        if (WasDisposed)
                            return null;

                        return _actualValue;
                    }
				}
			#endregion
		#endregion
	}
}
